package com.lemon.exam.common.util;

import com.lemon.exam.common.constant.ApiConst;
import com.lemon.exam.common.exception.CustomException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 验证工具类
 *
 * @author Lemon
 * @since 2025/4/15 16:28
 */
public class ValidatorUtil {
    /**
     * 验证对象
     *
     * @param validator
     * @param
     * @param <T>
     * @return
     */
    public static <T> Mono<T> validate(Validator validator, T object) {
        return validate(validator, object, ApiConst.EMPTY_STRING_ARRAY);
    }

    /**
     * 验证对象
     *
     * @param validator
     * @param <T>
     * @param propertiesToExclude 不验证的属性
     * @return
     */
    public static <T> Mono<T> validate(Validator validator, T object, String... propertiesToExclude) {
        return Mono.fromCallable(() -> filterViolations(validator.validate(object), propertiesToExclude))
                .flatMap(violations -> {
                    if (violations.isEmpty()) {
                        return Mono.just(object);
                    } else {
                        String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(", "));
                        return Mono.error(new CustomException(message));
                    }
                }).subscribeOn(Schedulers.boundedElastic());
    }

    /**
     * 过滤掉不需要验证的属性的违规结果
     *
     * @param violations
     * @param propertiesToExclude
     * @param <T>
     * @return
     */
    private static <T> Set<ConstraintViolation<T>> filterViolations(Set<ConstraintViolation<T>> violations, String[] propertiesToExclude) {
        if (propertiesToExclude == null || propertiesToExclude.length == 0) {
            return violations;
        }
        return violations.stream()
                .filter(violation -> !Arrays.asList(propertiesToExclude).contains(violation.getPropertyPath().toString()))
                .collect(Collectors.toSet());
    }


    /**
     * 验证对象属性
     *
     * @param validator
     * @param object
     * @param propertyName
     * @param <T>
     * @return
     */
    public static <T> Mono<T> validateProperty(Validator validator, T object, String propertyName) {
        return Mono.fromCallable(() -> validator.validateProperty(object, propertyName)).flatMap(violations -> {
            if (violations.isEmpty()) {
                return Mono.just(object);
            } else {
                String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
                return Mono.error(new CustomException(message));
            }
        }).subscribeOn(Schedulers.boundedElastic());
    }
}
